import javafx.concurrent.Service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class TcpEchoServer {
	ServerSocket  serverSocket=null;

	public TcpEchoServer(int prot) throws IOException {
		serverSocket=new ServerSocket(prot);
	}
	public void start() throws IOException {
		ExecutorService executorService= Executors.newCachedThreadPool();
		//服务器启动
		System.out.println("服务器启动");
		while(true) {
			//通过accept方法，把内核中已经建立好的链接拿到应用程序中
			//建立链接的细节流程都是内核自己自动完成的，程序只需要捡现成的。
			Socket clientSocket=serverSocket.accept();
			//此处不应该直接调用processconnection会导致服务器不能处理多个客户端
			//因为服务器在读取Inprustream中的数据的时候，服务器代码就会阻塞在客户端发送请求哪里，直到这个循环结束了
			//导致服务器第一时间无法接受第二个客户端发来的accept的请求
			Thread t1=new Thread(()->{
				try {
					processConnecion(clientSocket);
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			});
			t1.start();
		}
	}

	private void processConnecion(Socket clientSocket) throws IOException {
		System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
		//下面就是进行数据交互
		try(InputStream inputStream=clientSocket.getInputStream();
			OutputStream outputStream=clientSocket.getOutputStream()) {
			//使用try的方式以免后续忘记关闭流对象
			//由于客户端发来的数据，可能是多条数据，针对多条数据就循环的处理
			while(true) {
				Scanner scanner=new Scanner(inputStream);
				if(!scanner.hasNext()) {
					//链接断开，就应该直接客户端下线了


					//很多客户端链接的时候，此时就意味着每个链接都会创建socketCilent这个对象
					//当客户端下线的时候，socketCilen这个时候就失去了作用
					//如果这个时候没有手动进行关闭这个文件，这个socket对象就会占据着文件描述符的位置
					System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
					break;
				}
				//1.读取请求解析，此处就以next来作为读取请求的方式， next的规则就是，读取到空白符就返回
				String request=scanner.next();
				//2.根据请求，计算响应
				//这里做的是回显服务器
				String response=process(request);
				//3.把响应写回到客户端
				//可以把String转成字节数组，写入到OutputStream
				//也可以使用PrintWriter把outputstream包裹一下，来写入字符串
				PrintWriter printWriter=new PrintWriter(outputStream);
				//此处println 不是打印到，控制台，而是写入到 outputstream 对应的流对象中，也就是写到clientServer中
				//自然这个数据通过网络发送出去了
				//带有\n 也是为了后续客户端这边，也可以使用scanner.next来读取数据
				printWriter.println(response);
				printWriter.flush();
				//4.此处还要记得有个操作，记得刷新缓冲区，如果没有刷新操作，可能数据仍在内存中，没有被写入到网卡中
				System.out.printf("[%s:%d] request:%s response:%s\n", clientSocket.getInetAddress(), clientSocket.getPort(),request,response);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			//这里记得关闭这个文件，为什么是文件呢？因为操作系统在操控网卡的时候，把网卡当作文件再进行操作的
			//在有很多的客户端上线的时候，会频繁的创建这个对象，如果不关闭文件的话，会造成文件资源泄露
			//finally中无论如何都要进行执行
			//那为什么不在上面调用完processconnection 进行关闭文件呢

			//因为在processconnection中如果异常出现了，就来不及进行关闭文件程序就异常退出了
			//还是那一句话，客户端很多，就会造成文件资源泄露
			clientSocket.close();
		}
	}

	public String process(String request) {
		return request;
	}

	public static void main(String[] args) throws IOException {
		TcpEchoServer tcpEchoServer=new TcpEchoServer(9090);
		tcpEchoServer.start();
	}
}
